发现智能合约漏洞 - 游戏Dapp能作弊?
米芽:游戏宅女 / 独立游戏中毒者 / 全机种制霸 / 12年挖矿17年炒币 / Dapp游戏探索者 / 金牛座爱钱 / 自嘲又毒舌
前言
目前游戏Dapp的开发大多仍是以小作坊、小工作室、个人为主,主流的游戏公司在观望地同时也开始了小步快跑,很多联系到米芽一直在探讨链上链下的游戏机制如何设计、区块链游戏2.0时代等问题。
而包含游戏机制的智能合约如出现漏洞,小则无伤大雅,大则殃及用户和开发团队,米芽最近在1.7w份开源智能合约中寻觅打猎,试图发现一些典型例子,写出来给各位社区的朋友们参考,避免踩坑。
正文
米芽来讲讲智能合约的小漏洞,这个话题非常大,所以打算做成一个系列专题慢慢讲。相信大家之前也看到很多关于智能合约漏洞之类的文章,大部分都是只讲概念,不说例子,很没劲,对初学者来说,通篇的汉字都认识,连起来还是不明白。米芽的这个系列,希望用相对浅显易懂的语言来解释给大家,在技术层面的一些细节和严谨性问题咱们就不做深究了(技术大神们轻喷哈)。
从米芽的角度,会把漏洞根据影响分成三类:
无伤大雅型
埋坑害人型
弥天大错型
今天先讲讲第一类:无伤大雅型
智能合约,通俗一点的讲,就是在以太坊上进行交易时所触发的一段代码,这里面可以包含若干个函数,在每笔交易中会明确调用哪一个函数以及相关的参数。智能合约可以由合约作者来选择开源代码,或者不开源。
无伤大雅型的漏洞,意指不会给用户带来实际的代币损失但又存在一些可以投机取巧的空间。下面来讲一个网页前端和合约逻辑不匹配,从而影响游戏公平性的例子。
春节期间,米芽在玩游戏Dapp的时候注意到这样一款,WorldCryptoCup(虚拟世界杯)
这个游戏的本质依旧是强制买卖类Dapp,玩家可以用 ETH 来买卖参与世界杯的球队或者球员,后买者要付出比之前买家更多的 ETH 买入,收益都归上一个买家,最后如果没人接盘的话就等于砸到手里。
对于游戏Dapp还不熟悉的读者可以先看看米芽的第一篇文章——《ICO进入红海,新的赚钱机器——游戏Dapp悄悄崛起》
游戏刚上线时,32支队伍全部上线,一天晚上的时间内,从0.4-6ETH的价格炒到了最高204ETH的价格,球员则是定期通过倒计时的方式慢慢放出,倒计时完毕刚出来的时候价格较低,大家会疯抢,逐渐抬高。
倒不是这个机制多么有意思,反倒是在官方规则里面看到这么一段话 。
↓↓↓ 翻译一下 ↓↓↓
能不能看看你们的智能合约?
买卖球队的智能合约在这里可以看到,而且开源。
买卖球员的智能合约在这里,但并不开源。如果开源这份代码的话,玩家就可以在我们倒计时之前购买球员,这肯定会毁掉我们的游戏体验的。
米芽看完后表示,这是赤裸裸的告诉我你的合约可以让我们作弊,但你把合约代码藏起来了嘛。在翻译成人话说一遍就是,球员会提前在合约中被创建,但在倒计时之后才会显示在网页上给大家购买,如果你知道怎么调用合约里对应的球员购买函数,就可以在倒计时结束之前比别人先发起交易,优先以最低价买到这个球员。
那么如何能调用合约里对应的球员购买函数呢?知识点来了,大家拿小本本记好。
调用合约需要两个信息
智能合约地址:
跟钱包地址类似,上述的买卖球员合约地址为:
0x25289699a6cd8ff15b2cae6b6a51bfadfb118863
合约ABI(合约应用程序二进制接口):
用来明确调用时有哪些函数以及对应的参数
翻译一下,举个“不完全恰当”的例子来类比, 合约地址就好比米芽家四室两厅大平层的地址,家里有5个门,每个门上都有锁,有的是密码锁,有的是电子锁,那么ABI是什么呢,可以理解成是一份说明,告诉你这五个门对应着那个房间,进去这个房间你需要用什么样的钥匙才能打开。
对比着梳理一遍
知道米芽家的地址,拿着一份门锁说明,就知道用什么样的钥匙打开哪一扇门,从而就可以进你想进的房间。
知道智能合约地址,拿着合约ABI,就知道传入哪些参数来调用哪一个函数,从而就可以完成合约上的特定交易。
是不是瞬间清晰了?
注:类比不完全恰当,技术深究党别介意
下面我们来获取调用合约中的函数所需的两个信息,合约地址是显而易见的,一般游戏Dapp都会公布自己的合约地址,就算网站上没写,我们随便点一个与合约发生交互的按钮(比如购买球员),不需要完成这笔交易,Metamask就会显示出合约的地址。搞定!
寻找合约ABI
合约ABI在这里是一个问题,因为只有开源的合约,才能够在etherscan上面查到ABI信息。不开源的合约,etherscan上是没有的。
思考了一下,在网页实现合约调用的时候,也同样是需要ABI的信息呀?那会不会在网页加载时的某个配置文件中?顺着这个思路,
米芽成功的找到了!
在官网首页,如上图
点击右键选择Inspect(Chrome浏览器),然后刷新整个页面
点击Network
点击JS
找到config.js,双击
见证奇迹的时刻到了
在配置文件中,configAbiWorldCryptoCupPlayers 这个参数就是买卖球员合约的ABI。那么,该有的都有了,下面咱们来调用合约。
合约调用
以太坊的客户端以及部分钱包都是可以直接调用智能合约的,考虑到初学者和非技术的小伙伴,米芽就介绍不用写代码、只需要复制粘贴就能调用合约的钱包——myetherwallet。
使用地址(仅限PC端装有Metamask的Chrome或Firefox浏览器):
https://www.myetherwallet.com/#contracts
按照上图操作:
复制粘贴合约地址
0x25289699a6cd8ff15b2cae6b6a51bfadfb118863
复制粘贴合约ABI,刚刚配置文件config.js里面那一长段代码
点击Access
在Select a function(选择函数)的地方,我们就能看到所有的函数
其中,蓝框中两个函数是我们感兴趣的
getPlayer(获取球员信息)
这个函数的输入变量只有一个,就是球员编号,对照游戏页面上的信息,内马尔在前端展示的编号是1,但一般在以太坊上面,数列都是从0开始计数的,我们来输入0试试看,点击Read,返回的信息有四个,playerId(球员ID),name(球员名字),ownerAddress(拥有者地址),curPrice(当前价格)。果不其然,在链上的第0号球员就是内马尔,目前的价格9.397265625 ETH 也与curPrice相对应(注,以太坊是不支持小数的,ETH留了十八个小数位,所以curPrice这个数字要除以1000000000000000000 - 18个零,才是我们看到的ETH真正价格)
purchasePlayer(购买球员)
这个函数也就是我们要用来“作弊”的函数啦~~只需要填入球员ID,就可以发起交易。注意发起交易时,价格必须是上面的curPrice,也就是球员信息中的价格,如果使用低于这个价格发起交易,那么就会交易失败。
实例分析
在3月1日12:47PM左右,游戏开发组连着发起了四笔金额为0的交易(看上图红框部分),同时又在Discord群公告,2小时后将有新球员空投。那么不难猜测,这四笔交易就是开发者调用了createPlayer(创建球员)函数,创建出来的4个新球员,官网上倒计时则设置成2个多小时,也就是在3PM准时页面上放出球员卡片。蓝框中,第一笔交易完成的时间是3:00:23PM,也就是说大部分玩家都是在3点后开始的交易,但其实从球员创建之后到3PM这段时间中,通过上述的方法,都是可以“作弊”提前交易球员的。
正确的“作弊”姿势
1. 看到官方Discord上发出新球员的预告,去合约地址确认是否已经创建
2. 在官网查看目前球员的最新编号是多少(比如是60)
3. 调用getPlayer函数,查看新球员的价格(比如试一下60,61,62这些球员ID)
4. 在倒计时结束前10秒左右,调用purchasePlayer函数,用对应球员价格购买新球员
为什么是在倒计时结束前10秒左右而不是更早?
如果更早发起交易,这笔交易的结束时间会落在倒计时完成之前,开发团队看到记录后就会发现你在作弊。如果只是提前10秒,根据目前以太坊的交易速度,处理完后基本会落在倒计时之后,但你比其他对手抢先了10秒的优势,只要gas price不要设置的太低,这个球员基本都是你的啦。只要不细查,或者实时监控,开发者只会以为你是一个手速极快的选手,而非作弊者。
上述合约问题,怎么解决?
米芽能想到的一个方案是比较简单的:
在createPlayer(创建球员)函数中,给每个球员增加一个参数unlockTime(解锁时间),记录一个未来的时间点。官网上倒计时就是按照这个时间点开始倒计时。
在purchasePlayer(购买球员)函数中,加一句判断
require(player.unlockTime <= now)
这句代码的意思就是要求现在的时间必须大于球员的解锁时间,才能继续运行购买函数,否则函数将运行失败。
注:此处假设所有的球员是在一个struct array里面,unlockTime是player struct中的一个变量。另外在以太坊上now等于blocks.timestamp , 不完全是等于现在的时间,从理论上,矿工是可以操纵timestamp的。如果有大神有更好的方案,欢迎留言哈
彩蛋!
你以为WorldCryptoCup只有这一个小bug么?
淘气的米芽发现,对于这类可以设置昵称,但是没有账户登录体系的游戏dapp,其实可以随便改掉其他人的昵称。如下图中,米芽把其他人的昵称全部都改掉了。(⊙﹏⊙)
实现方式其实就是找到修改昵称的request,因为没有登录系统,也没有accessToken一说,我就说到这里,稍微懂点技术的朋友应该能明白怎么回事了,具体实现就不一步步写出来了。毕竟有点恶作剧的成分哈。
下期高能预警:怒喷伪区块链游戏及其套路!
上期 ETH 中奖地址
0x089D....DEB1
0x585D....232A
米芽小助理已经打币
响应粉丝号召,米芽创建了一个游戏Dapp交流群,欢迎游戏开发者,玩家加入一起讨论新的游戏Dapp以及技术交流。
后台回复“加群”,小助理将拉你进群。
关注米芽DappReview,你能获得什么?
各类游戏Dapp评测
最新最快的游戏Dapp指南,一起打新一起赚钱
游戏Dapp开发方向和真知灼见
汇集了米芽从小到大近3000游戏涉猎的结晶
游戏Dapp黑名单
哪些游戏千万别投资了,哪些游戏目测要跑路
智能合约里面的坑
哪些游戏的合约有漏洞?可以作弊?
粉丝 ETH 抽奖计划
免费送钱你要不要?